import {
  EventsRes,
  ICurve,
  ILineParams,
  ParameterObjectType,
  SceneGraph,
} from '@amcax/base';
import { ctx2 } from '@app-cad/module';

import { Object3D, Vector2, Vector3 } from 'three-legacy';
import {
  SKETCH_CENTEREVENTS,
  sketchEventCenter,
} from '../../../events/SketchEventCenter';
import CmdBase from '../../CmdBase';
import { wsApi } from '@app-cad/server';
import {
  calcArcCircleCenter,
  calcLineLength,
  getLineIntersectionTwo,
  renderSketch,
  sketchPreview,
  updataRenderDim,
} from '@app-cad/common/utils';
import { DimCss2dInputLable } from '../dimension/dimTools/label';
import { MouseState, State } from './types';

export class CmdInteractor extends CmdBase {
  point: Vector3;
  private clickStatus: boolean;
  private selectData: ICurve[];
  private radius: number;
  private maxRadius: number;
  private center: Vector2;
  lastParams: string[];
  label: DimCss2dInputLable;
  pickInfo: ICurve;
  currentState: MouseState;
  // select 上的同等距离的点
  selectPoint: Vector2;
  toolsGroup: Object3D;
  previewData;
  previewStatus: boolean;
  blurStatus: boolean;
  //第一次选中线上的点
  firstSelectPoint: Vector3;
  //第一次选中的点到交点的距离
  firstSelectPointDistance: number;
  //  交点
  interSection: Vector2;
  contactPoint: Vector2;
  constructor() {
    super();
    this.clickStatus = false;
    this.selectData = [];
    this.center = null;
    this.lastParams = [];
    this.radius = 200;
    this.pickInfo = null;
    this.toolsGroup = ctx2.cadScene.toolsGroup;
    this.previewData = null;
    this.previewStatus = false;
    this.maxRadius = null;
    this.registerEvents();
  }

  reset() {
    this.clickStatus = false;
    this.selectData = [];
    this.pickInfo = null;
    this.radius = null;
    this.label = null;
    this.label?.destroy();
  }

  registerEvents() {
    sketchEventCenter()
      .setState('fillet')
      .on(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .on(SKETCH_CENTEREVENTS.POINTERUP, this.onMouseUp)
      .on(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .addExit(this.destroy.bind(this));
  }

  unregisterEvents() {
    sketchEventCenter()
      .off(SKETCH_CENTEREVENTS.POINTERMOVE, this.onMouseMove)
      .off(SKETCH_CENTEREVENTS.POINTERUP, this.onMouseUp)
      .off(SKETCH_CENTEREVENTS.KEYUP, this.onKeyUp)
      .setDefault();
  }
  destroyLabel(): void {
    this.label = null;
    this.label?.destroy();
  }
  destroyPreview(): void {
    this.assisGroup.clear();
    this.picker.clickNull();
    this.toolsGroup.clear();
    SceneGraph.emptyGroup(this.assisGroup);
  }

  releaseObjects() {
    this.destroyLabel();
    this.destroyPreview();
    ctx2.viewer.requestRender();
  }

  enterKey = async (status: string, val: number) => {
    if (status === 'blur' && val) {
      sketchEventCenter().off(
        SKETCH_CENTEREVENTS.POINTERMOVE,
        this.onMouseMove,
      );

      this.radius = this.label.getText();
      const ids = this.selectData.map((item) => item.id);
      if (ids.length > 0 && this.pickInfo && !ids.includes(this.pickInfo.id)) {
        const res = await wsApi.filletSketchCurve(
          this.sketchId,
          [this.pickInfo.id, ...ids],
          2,
          this.label.textParam || this.radius,
          false,
          this.contactPoint
            ? [this.contactPoint.x, this.contactPoint.y]
            : [this.point.x, this.point.y],
          this.maxRadius
            ? [this.firstSelectPoint.x, this.firstSelectPoint.y]
            : [this.selectPoint.x, this.selectPoint.y],
        );
        this.destroyLabel();
        await renderSketch(this.sketchId, res);
        await updataRenderDim(this.sketchId);
        this.reset();
        this.destroyPreview();

        this.currentState = State.NONE;
        sketchEventCenter().on(
          SKETCH_CENTEREVENTS.POINTERMOVE,
          this.onMouseMove,
        );
      }
    }
  };

  onMouseMove = async (e: EventsRes) => {
    ctx2.cadScene.assisGroup.clear();
    const info = this.picker.pick(e);
    this.pickInfo = info;
    // this.setMouseWV(e, false, false);
    if (this.selectData.length > 0 && info) {
      this.point = this.getPointByMouse(e.mouse).clone();
      const ids = this.selectData.map((item) => item.id);
      const currentParams: string[] = [info?.id, ...ids];
      const current = [info, ...this.selectData];
      if (
        !ids.includes(info.id) &&
        info.type === 'Line' &&
        info.id !== 'X-AXIS' &&
        info.id !== 'Y-AXIS' &&
        this.selectData[0].type === 'Line'
      ) {
        // 直线和直线做圆角
        const p1 = new Vector2(
          (current[0].params as ILineParams).point1[0],
          (current[0].params as ILineParams).point1[1],
        );
        const p2 = new Vector2(
          (current[0].params as ILineParams).point2[0],
          (current[0].params as ILineParams).point2[1],
        );
        const p3 = new Vector2(
          (current[1].params as ILineParams).point1[0],
          (current[1].params as ILineParams).point1[1],
        );
        const p4 = new Vector2(
          (current[1].params as ILineParams).point2[0],
          (current[1].params as ILineParams).point2[1],
        );
        const crossInfo = getLineIntersectionTwo(p1, p2, p3, p4);

        // 交点坐标
        const interSection: Vector2 = new Vector2(crossInfo.x, crossInfo.y);
        this.firstSelectPointDistance = calcLineLength(
          interSection,
          new Vector2(this.firstSelectPoint.x, this.firstSelectPoint.y),
        );
        this.interSection = interSection;
        const distance = calcLineLength(
          interSection,
          new Vector2(this.point.x, this.point.y),
        );

        // 第一次选中点到交点的向量
        const firstSelectPointVec = new Vector2()
          .subVectors(
            new Vector2(this.firstSelectPoint.x, this.firstSelectPoint.y),
            interSection,
          )
          .normalize();
        const selectPoint = new Vector2().addVectors(
          interSection,
          firstSelectPointVec.multiplyScalar(distance),
        );
        this.selectPoint = selectPoint;

        const pickVec = new Vector2()
          .subVectors(new Vector2(this.point.x, this.point.y), interSection)
          .normalize();

        if (distance < this.firstSelectPointDistance) {
          // 圆心
          const { circleCenter, circleRadius } = calcArcCircleCenter(
            this.selectPoint,
            new Vector2(this.point.x, this.point.y),
            interSection,
          );

          this.radius = circleRadius;
          this.center = circleCenter;
          if (!this.label) {
            this.createInputLabel(circleRadius);
          } else {
            this.updateInputLabel(circleRadius);
          }
          if (!this.previewStatus) {
            this.previewStatus = true;
            const res = await wsApi.filletSketchCurve(
              this.sketchId,
              currentParams,
              2,
              circleRadius,
              true,
              [this.point.x, this.point.y],
              [this.selectPoint.x, this.selectPoint.y],
            );
            if (res && res.curves) {
              sketchPreview(res.curves);
              this.previewStatus = false;
            }
          }
        } else {
          // 计算鼠标到交点的向量在不在直线上
          //  直线向量
          const lineVec = new Vector2().subVectors(p2, p1).normalize();
          const determinant = pickVec.x * lineVec.y - pickVec.y * lineVec.x;
          let projectionVec;
          if (Math.abs(determinant) > Number.EPSILON) {
            // 不共线，需要将鼠标的点投影到直线向量上来消除误差
            const projectionLength = pickVec.dot(lineVec);

            // 计算投影向量
            const projectionVec1 = new Vector2()
              .copy(lineVec)
              .multiplyScalar(projectionLength);

            // 计算投影点
            const projectionPoint = new Vector2().addVectors(
              interSection,
              projectionVec1,
            );
            // 交点到投影点的向量
            projectionVec = new Vector2().subVectors(
              projectionPoint,
              interSection,
            );
          } else {
            projectionVec = new Vector2()
              .subVectors(new Vector2(this.point.x, this.point.y), interSection)
              .normalize();
          }
          // 超出最大半径，以一个选中的点为准
          const contactPoint = new Vector2().addVectors(
            interSection,
            projectionVec.multiplyScalar(this.firstSelectPointDistance),
          );
          this.contactPoint = contactPoint;

          const { circleCenter, circleRadius } = calcArcCircleCenter(
            new Vector2(this.firstSelectPoint.x, this.firstSelectPoint.y),
            contactPoint,
            interSection,
          );
          this.maxRadius = circleRadius;
          this.center = circleCenter;
          if (!this.label) {
            this.createInputLabel(circleRadius);
          } else {
            this.updateInputLabel(circleRadius);
          }
          if (!this.previewStatus) {
            this.previewStatus = true;
            const res = await wsApi.filletSketchCurve(
              this.sketchId,
              currentParams,
              2,
              circleRadius,
              true,
              [contactPoint.x, contactPoint.y],
              [this.firstSelectPoint.x, this.firstSelectPoint.y],
            );
            if (res && res.curves) {
              sketchPreview(res.curves);
              this.previewStatus = false;
            }
          }
        }
      }
    } else {
      this.assisGroup.clear();
      ctx2.viewer.requestRender();
      this.label?.destroy();
      this.label = null;
      this.lastParams = [];
      this.toolsGroup.clear();
      this.previewData?.destroy();
      this.previewData = null;
      this.maxRadius = null;
      this.radius = null;
      this.contactPoint = null;
      this.previewStatus = false;
    }
  };
  createInputLabel(radius?: number) {
    this.label = new DimCss2dInputLable({
      position: new Vector3(
        this.point.x + 30 / ctx2.getCameraZoom(),
        this.point.y + 30 / ctx2.getCameraZoom(),
        0,
      ),
      text: radius,
      sketchFrame: this.sketchFrame,
      callback: this.enterKey,
      inputType: ParameterObjectType.Length,
    });
    this.label.addTo(this.toolsGroup);
    this.label.clickFocus();
    this.label.focus();
    ctx2.viewer.requestRender();
  }
  updateInputLabel(radius?: number) {
    this.label.update({
      pos: new Vector3(
        this.point.x + 30 / ctx2.getCameraZoom(),
        this.point.y + 30 / ctx2.getCameraZoom(),
        0,
      ),
      text: radius,
    });
  }
  onMouseUp = async ({ button, event, mouse }: EventsRes<PointerEvent>) => {
    if (button.onLeft) {
      const data: ICurve = this.picker.select(event, mouse);
      if (!this.clickStatus) {
        if (data) {
          this.point = this.getPointByMouse(mouse).clone();
          this.selectData.push(data);
          this.firstSelectPoint = this.point;

          this.clickStatus = true;
        }
      }
    } else if (button.onRight) {
      // 鼠标右键，取消绘制
      this.reset();
      this.releaseObjects();
    }
  };
}
